package com.gugee.tiktok.web.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.gugee.common.web.util.Ret;
import com.gugee.common.web.config.exception.ParamsException;
import com.gugee.common.web.system.promise.Limit;
import com.gugee.common.web.system.promise.RequestLimit;
import com.gugee.common.web.system.user.service.UserService;
import com.gugee.tiktok.web.dto.RankSearchData;
import com.gugee.tiktok.web.model.mysql.BloggerType;
import com.gugee.tiktok.web.service.BloggerService;
import com.gugee.tiktok.web.service.RankingService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.time.LocalDateTime;
import java.util.*;

@RestController
@Api(value = "排行榜",tags = {"排行榜"})
public class RankingController extends TikTokBaseController {

    @Autowired
    RankingService rankingService;

    @Autowired
    BloggerService bloggerService;

    @Autowired
    UserService userService;

    //===================================================博主排行榜 START===================================================

    @ApiOperation(value = "获取博主排行榜")
    @RequestMapping(value = "ranking/blogger",method = RequestMethod.GET,produces = "application/json;charset=UTF-8")
    @RequestLimit(value = "PageDataLimit",property = "page,pageSize",validateType = Limit.VIEW_DATA_LIMIT)
    public String getRankingList(String data) throws UnsupportedEncodingException, ParamsException {
        RankSearchData searchData = getParam(RankSearchData.class, URLDecoder.decode(data,"UTF-8"));

        if(null  == searchData.getDate()){
         searchData.setDate(getDefaultDate(searchData.getRankingType()));
        }
        val pageRanking = rankingService.getPageRanking(searchData, getUser().getId());
        return Ret.pageData(pageRanking.getTotalRow(),pageRanking.getList());
    }

    @ApiOperation(value = "获取博主排行榜时间日期")
    @RequestMapping(value = "ranking/date",method = RequestMethod.GET,produces = "application/json;charset=UTF-8")
    public String getRankingDate(){
        var dailyDate = DateUtil.parseLocalDateTime(String.valueOf(getDefaultDate(1)),"yyyyMMdd").minusMonths(1);
        var weeklyDate = DateUtil.parseLocalDateTime(String.valueOf(getDefaultDate(2)),"yyyyMMdd").minusMonths(3);
        var monthlyDate = DateUtil.parseLocalDateTime(String.valueOf(getDefaultDate(3)),"yyyyMMdd").minusYears(1);

        val rankingDate = rankingService.getRankingDate(Integer.parseInt(DateUtil.format(dailyDate, "yyyyMMdd")),
                Integer.parseInt(DateUtil.format(weeklyDate, "yyyyMMdd")),
                Integer.parseInt(DateUtil.format(monthlyDate, "yyyyMMdd")));
        return Ret.success("OK",rankingDate);
    }

    @ApiOperation(value = "获取博主排行榜更新时间")
    @RequestMapping(value = "ranking/update",method = RequestMethod.GET,produces = "application/json;charset=UTF-8")
    public String getRankingUpdate(){
        return Ret.success("OK",rankingService.getRankingUpdateTime());
    }


    @ApiOperation(value = "导出博主排行榜")
    @RequestMapping(value = "ranking/export",method = RequestMethod.GET)
    public void exportRanking(String data, HttpServletResponse response) throws ParamsException, IOException {
        RankSearchData searchData = getParam(RankSearchData.class, URLDecoder.decode(data,"UTF-8"));

        if(null  == searchData.getDate()){
            searchData.setDate(getDefaultDate(searchData.getRankingType()));
        }
        String fileName = exportFilename(searchData.getRankingType(),searchData.getBloggerTypeId(),searchData.getDate());

        if(null  == searchData.getDate()){
            searchData.setDate(getDefaultDate(searchData.getRankingType()));
        }

        int count = 1000;
        val userRole = userService.getUserRole(getUser().getId(), new Date());

        // advenced
        if(null != userRole && userRole.getRoleId() == 3){
            count = 500;
        }
        val rankings = rankingService.getRankingList(searchData,count);
        ExcelWriter writer = ExcelUtil.getWriter();
        String [] columns = new String[]{"Rank","Name","Username","GD Index","Followers","Likes","Avg View","ER"};

        if(searchData.getRankingType() == 1){
            columns = new String[]{"Rank","Name","Username","GD Index","Followers","Likes","Avg View","ER"};
        }
        List<Map<String,Object>> rows = new ArrayList<>();
        String[] finalColumns = columns;
        rankings.forEach(ranking -> {
            Map<String,Object> row = new LinkedHashMap<>();
            row.put(finalColumns[0], ranking.getRanking());
            row.put(finalColumns[1], ranking.getNickname());
            row.put(finalColumns[2], ranking.getUniqueId());
            row.put(finalColumns[3], ranking.getScore());
            row.put(finalColumns[4], ranking.getFollower());
            row.put(finalColumns[5], ranking.getLikes());
            row.put(finalColumns[6], ranking.getAvgView());
            row.put(finalColumns[7], ranking.getEngagementRate());
            rows.add(row);
        });
        writer.write(rows,true);
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Disposition","attachment;filename="+fileName.replace("{type}","Industrial")+".xls");
        writer.flush(response.getOutputStream());
        writer.close();
    }

    //===================================================博主排行榜 END===================================================

    //===================================================博主涨粉排行榜 START===================================================

    @ApiOperation(value = "获取博主涨粉排行榜")
    @RequestMapping(value = "ranking/follower",method = RequestMethod.GET,produces = "application/json;charset=UTF-8")
    @RequestLimit(value = "PageDataLimit",property = "page,pageSize",validateType = Limit.VIEW_DATA_LIMIT)
    public String getRankingFollowersList(String data) throws UnsupportedEncodingException, ParamsException {
        RankSearchData searchData = getParam(RankSearchData.class, URLDecoder.decode(data,"UTF-8"));

        if(null  == searchData.getDate()){
            searchData.setDate(getDefaultDate(searchData.getRankingType()));
        }
        val pageRankingFollowers = rankingService.getPageRankingFollowers(searchData, getUser().getId());
        return Ret.pageData(pageRankingFollowers.getTotalRow(),pageRankingFollowers.getList());
    }



    @ApiOperation(value = "获取博主涨粉排行榜更新时间")
    @RequestMapping(value = "ranking/follower/update",method = RequestMethod.GET,produces = "application/json;charset=UTF-8")
    public String getRankingFollowerUpdate(){
        return Ret.success("OK",rankingService.getRankingFollowerUpdateTime());
    }



    @ApiOperation(value = "获取博主涨粉排行榜时间日期")
    @RequestMapping(value = "ranking/follower/date",method = RequestMethod.GET,produces = "application/json;charset=UTF-8")
    public String getRankingFollowerDate(){
        var dailyDate = DateUtil.parseLocalDateTime(String.valueOf(getDefaultDate(1)),"yyyyMMdd").minusMonths(1);
        var weeklyDate = DateUtil.parseLocalDateTime(String.valueOf(getDefaultDate(2)),"yyyyMMdd").minusMonths(3);
        var monthlyDate = DateUtil.parseLocalDateTime(String.valueOf(getDefaultDate(3)),"yyyyMMdd").minusYears(1);

        val rankingFollowerDate = rankingService.getRankingFollowerDate(Integer.parseInt(DateUtil.format(dailyDate, "yyyyMMdd")),
                Integer.parseInt(DateUtil.format(weeklyDate, "yyyyMMdd")),
                Integer.parseInt(DateUtil.format(monthlyDate, "yyyyMMdd")));
        return Ret.success("OK",rankingFollowerDate);
    }

    @ApiOperation(value = "导出博主涨粉排行榜")
    @RequestMapping(value = "ranking/follower/export",method = RequestMethod.GET)
    public void exportRankingFollower(String data, HttpServletResponse response) throws ParamsException, IOException {
        RankSearchData searchData = getParam(RankSearchData.class, URLDecoder.decode(data,"UTF-8"));

        if(null  == searchData.getDate()){
            searchData.setDate(getDefaultDate(searchData.getRankingType()));
        }
        String fileName = exportFilename(searchData.getRankingType(),searchData.getBloggerTypeId(),searchData.getDate());

        if(null  == searchData.getDate()){
            searchData.setDate(getDefaultDate(searchData.getRankingType()));
        }

        int count = 1000;
        val userRole = userService.getUserRole(getUser().getId(), new Date());

        if(null != userRole && userRole.getRoleId() == 3){
            count = 500;
        }
        val rankingFollowers = rankingService.getRankingFollowersList(searchData,count);
        ExcelWriter writer = ExcelUtil.getWriter();
        String [] columns = new String[]{"Rank","Name","Username","Followers Growth","Total Followers"};
        List<Map<String,Object>> rows = new ArrayList<>();
        rankingFollowers.forEach(ranking -> {
            Map<String,Object> row = new LinkedHashMap<>();
            row.put(columns[0], ranking.getRanking());
            row.put(columns[1], ranking.getNickname());
            row.put(columns[2], ranking.getUniqueId());
            row.put(columns[3], ranking.getFollowerGrowth());
            row.put(columns[4], ranking.getFollower());
            rows.add(row);
        });
        writer.write(rows,true);
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Disposition","attachment;filename="+fileName.replace("{type}","Follower")+".xls");
        writer.flush(response.getOutputStream(),true);
        writer.close();
    }

    //===================================================博主涨粉排行榜 END===================================================


    //===================================================博主企业排行榜 START===================================================

    @ApiOperation(value = "获取博主企业排行榜")
    @RequestMapping(value = "ranking/enterprise/blogger",method = RequestMethod.GET,produces = "application/json;charset=UTF-8")
    @RequestLimit(value = "PageDataLimit",property = "page,pageSize",validateType = Limit.VIEW_DATA_LIMIT)
    public String getRankingEnterpriseList(String data) throws UnsupportedEncodingException, ParamsException {
        RankSearchData searchData = getParam(RankSearchData.class, URLDecoder.decode(data,"UTF-8"));

        if(null  == searchData.getDate()){
            searchData.setDate(getDefaultDate(searchData.getRankingType()));
        }
        val pageRanking = rankingService.getPageRankingEnterprise(searchData, getUser().getId());
        return Ret.pageData(pageRanking.getTotalRow(),pageRanking.getList());
    }

    @ApiOperation(value = "获取博主企业排行榜时间日期")
    @RequestMapping(value = "ranking/enterprise/date",method = RequestMethod.GET,produces = "application/json;charset=UTF-8")
    public String getRankingEnterpriseDate(){
        var dailyDate = DateUtil.parseLocalDateTime(String.valueOf(getDefaultDate(1)),"yyyyMMdd").minusMonths(1);
        var weeklyDate = DateUtil.parseLocalDateTime(String.valueOf(getDefaultDate(2)),"yyyyMMdd").minusMonths(3);
        var monthlyDate = DateUtil.parseLocalDateTime(String.valueOf(getDefaultDate(3)),"yyyyMMdd").minusYears(1);

        val rankingDate = rankingService.getRankingEnterpriseDate(Integer.parseInt(DateUtil.format(dailyDate, "yyyyMMdd")),
                Integer.parseInt(DateUtil.format(weeklyDate, "yyyyMMdd")),
                Integer.parseInt(DateUtil.format(monthlyDate, "yyyyMMdd")));
        return Ret.success("OK",rankingDate);
    }

    @ApiOperation(value = "获取博主企业排行榜更新时间")
    @RequestMapping(value = "ranking/enterprise/update",method = RequestMethod.GET,produces = "application/json;charset=UTF-8")
    public String getRankingEnterpriseUpdate(){
        return Ret.success("OK",rankingService.getRankingEnterpriseUpdateTime());
    }

    @ApiOperation(value = "导出博主企业排行榜")
    @RequestMapping(value = "ranking/enterprise/export",method = RequestMethod.GET)
    public void exportRankingEnterprise(String data, HttpServletResponse response) throws ParamsException, IOException {
        RankSearchData searchData = getParam(RankSearchData.class, URLDecoder.decode(data,"UTF-8"));

        if(null  == searchData.getDate()){
            searchData.setDate(getDefaultDate(searchData.getRankingType()));
        }
        String fileName = exportFilename(searchData.getRankingType(),searchData.getBloggerTypeId(),searchData.getDate());

        if(null  == searchData.getDate()){
            searchData.setDate(getDefaultDate(searchData.getRankingType()));
        }

        int count = 1000;
        val userRole = userService.getUserRole(getUser().getId(), new Date());

        // advenced
        if(null != userRole && userRole.getRoleId() == 3){
            count = 500;
        }
        val rankings = rankingService.getRankingEnterpriseList(searchData,count);
        ExcelWriter writer = ExcelUtil.getWriter();
        String [] columns = new String[]{"Rank","Name","Username","GD Index","Followers","Likes","Avg View","ER"};

        if(searchData.getRankingType() == 1){
            columns = new String[]{"Rank","Name","Username","GD Index","Followers","Likes","Avg View","ER"};
        }
        List<Map<String,Object>> rows = new ArrayList<>();
        String[] finalColumns = columns;
        rankings.forEach(ranking -> {
            Map<String,Object> row = new LinkedHashMap<>();
            row.put(finalColumns[0], ranking.getRanking());
            row.put(finalColumns[1], ranking.getNickname());
            row.put(finalColumns[2], ranking.getUniqueId());
            row.put(finalColumns[3], ranking.getScore());
            row.put(finalColumns[4], ranking.getFollower());
            row.put(finalColumns[5], ranking.getLikes());
            row.put(finalColumns[6], ranking.getAvgView());
            row.put(finalColumns[7], ranking.getEngagementRate());
            rows.add(row);
        });
        writer.write(rows,true);
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Disposition","attachment;filename="+fileName.replace("{type}","Enterprise")+".xls");
        writer.flush(response.getOutputStream());
        writer.close();
    }


    //===================================================博主企业排行榜 END===================================================


    //===================================================博主国家排行榜 START===================================================

    @ApiOperation(value = "获取博主国家排行榜")
    @RequestMapping(value = "ranking/region/blogger",method = RequestMethod.GET,produces = "application/json;charset=UTF-8")
    @RequestLimit(value = "PageDataLimit",property = "page,pageSize",validateType = Limit.VIEW_DATA_LIMIT)
    public String getRankingRegionList(String data) throws UnsupportedEncodingException, ParamsException {
        RankSearchData searchData = getParam(RankSearchData.class, URLDecoder.decode(data,"UTF-8"));

        if(null  == searchData.getDate()){
            searchData.setDate(getDefaultDate(searchData.getRankingType()));
        }
        val pageRanking = rankingService.getPageRankingRegion(searchData, getUser().getId());
        return Ret.pageData(pageRanking.getTotalRow(),pageRanking.getList());
    }

    @ApiOperation(value = "获取博主国家排行榜时间日期")
    @RequestMapping(value = "ranking/region/date",method = RequestMethod.GET,produces = "application/json;charset=UTF-8")
    public String getRankingRegionDate(){
        var dailyDate = DateUtil.parseLocalDateTime(String.valueOf(getDefaultDate(1)),"yyyyMMdd").minusMonths(1);
        var weeklyDate = DateUtil.parseLocalDateTime(String.valueOf(getDefaultDate(2)),"yyyyMMdd").minusMonths(3);
        var monthlyDate = DateUtil.parseLocalDateTime(String.valueOf(getDefaultDate(3)),"yyyyMMdd").minusYears(1);

        val rankingDate = rankingService.getRankingRegionDate(Integer.parseInt(DateUtil.format(dailyDate, "yyyyMMdd")),
                Integer.parseInt(DateUtil.format(weeklyDate, "yyyyMMdd")),
                Integer.parseInt(DateUtil.format(monthlyDate, "yyyyMMdd")));
        return Ret.success("OK",rankingDate);
    }

    @ApiOperation(value = "获取博主国家排行榜更新时间")
    @RequestMapping(value = "ranking/region/update",method = RequestMethod.GET,produces = "application/json;charset=UTF-8")
    public String getRankingRegionUpdate(){
        return Ret.success("OK",rankingService.getRankingRegionUpdateTime());
    }

    @ApiOperation(value = "导出博主国家排行榜")
    @RequestMapping(value = "ranking/region/export",method = RequestMethod.GET)
    public void exportRankingRegion(String data, HttpServletResponse response) throws ParamsException, IOException {
        RankSearchData searchData = getParam(RankSearchData.class, URLDecoder.decode(data,"UTF-8"));

        if(null  == searchData.getDate()){
            searchData.setDate(getDefaultDate(searchData.getRankingType()));
        }
        String fileName = exportFilename(searchData.getRankingType(),searchData.getBloggerTypeId(),searchData.getDate());

        if(null  == searchData.getDate()){
            searchData.setDate(getDefaultDate(searchData.getRankingType()));
        }

        int count = 1000;
        val userRole = userService.getUserRole(getUser().getId(), new Date());

        // advenced
        if(null != userRole && userRole.getRoleId() == 3){
            count = 500;
        }
        val rankings = rankingService.getRankingRegionList(searchData,count);
        ExcelWriter writer = ExcelUtil.getWriter();
        String [] columns = new String[]{"Rank","Name","Username","GD Index","Followers","Likes","Avg View","ER"};

        if(searchData.getRankingType() == 1){
            columns = new String[]{"Rank","Name","Username","GD Index","Followers","Likes","Avg View","ER"};
        }
        List<Map<String,Object>> rows = new ArrayList<>();
        String[] finalColumns = columns;
        rankings.forEach(ranking -> {
            Map<String,Object> row = new LinkedHashMap<>();
            row.put(finalColumns[0], ranking.getRanking());
            row.put(finalColumns[1], ranking.getNickname());
            row.put(finalColumns[2], ranking.getUniqueId());
            row.put(finalColumns[3], ranking.getScore());
            row.put(finalColumns[4], ranking.getFollower());
            row.put(finalColumns[5], ranking.getLikes());
            row.put(finalColumns[6], ranking.getAvgView());
            row.put(finalColumns[7], ranking.getEngagementRate());
            rows.add(row);
        });
        writer.write(rows,true);
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Disposition","attachment;filename="+fileName.replace("{type}","Region")+".xls");
        writer.flush(response.getOutputStream());
        writer.close();
    }
    //===================================================博主国家涨粉排行榜 END===================================================



    private String exportFilename(int rankingType,Integer type,Integer date){
        String fileName = "{categories}{type}GrowthDailyLeaderboard";
        switch (rankingType){
            case 2:
                fileName = "{categories}{type}GrowthWeeklyLeaderboard";
                break;
            case 3:
                fileName = "{categories}{type}GrowthMonthlyLeaderboard ";
                break;
            default:
                break;
        }

        if(null == type || 0 == type){

            fileName = fileName.replace("{categories}","ALL");
        }else{
            val typeList = bloggerService.getTypeList();
            for (BloggerType bloggerType : typeList) {
                if(bloggerType.getId() == type){
                    fileName = fileName.replace("{categories}",bloggerType.getName());
                    break;
                }
            }
        }
        return fileName + date.toString();
    }

    private int getDefaultDate(int ranking){
        int dateCode;
        switch (ranking){
            case 2:
                dateCode =  getLastWeekDay();
                break;
            case 3:
                dateCode = getLastMonthDay();
                break;
            default:
                dateCode = Integer.parseInt(DateUtil.format(LocalDateTime.now().minusDays(1),"yyyyMMdd"));
                break;
        }
        return dateCode;
    }


    private int getLastWeekDay(){
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE, -1*7);
        cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);

        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return Integer.parseInt(DateUtil.format(cal.getTime(),"yyyyMMdd"));
    }

    private int getLastMonthDay(){
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.MONTH, -1);
        cal.set(Calendar.DAY_OF_MONTH,1);

        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return Integer.parseInt(DateUtil.format(cal.getTime(),"yyyyMMdd"));
    }

}
